<?php
/**
 * EhDB.class.php, Classe de conexão com banco de dados
 *
 * Este arquivo faz parte do Framework EhModuleWeb
 *
 * @version 0.0
 * @author Erlimar Silva Campos <erlimar.campos@ehsistemas.com.br>
 * @date  Junho 19, 2011 23:00 PM
 *
 *
 * Copyright (C) 2011 EH SISTEMAS DE INFORMÁTICA ME
 * 
 */

class EhDB extends PDO {
    /* DBSN (DataBase Source Name) */
    private $DBSN           = null;
    
    /* Tipo de banco de dados usado, ex: MySQL, DB2, Firebird, MSSQL */
    private $Engine         = null;
    
    /* Host (servidor) onde o banco de dados está hospedado */
    private $Hostname       = null;
    
    /* Porta no servidor */
    private $Port           = null;

    /* Nome do banco de dados no servidor especificado */
    private $Database       = null;
    
    /* Nome do usuário usado na conexão com o banco de dados */
    private $Username       = null;
    
    /* Senha do usuário especificado para a conexão */
    private $Password       = null;
    
    /* Lista de parâmetros adicionais usados na conexão.
     * Os parâmetros são usados no padrão CHAVE => VALOR
     */
    private $Params         = null;
    
    /* Lista de parâmetros adicionais usados na conexão, específicos do PDO */
    private $PDOParams      = null;
    
    /**
     * Construtor padrão da classe
     *
     * @param   $dbsn        string, Database Source Name
     *          - Uma string de conexão com o banco de dados no padão:
     *            engine://user:password@hostname:port/database?PARAM1=VALUE1&PARAM2
     *
     *          - ex: "mysql://root:123456@localhost:3306/mydatabase"
     *            Este exemplo conecta em uma base "MySQL" em "localhost", na
     *            porta 3306, com usuário "root" e senha "123456"
     */
    public function __construct($dbsn)
    {
        $this->DBSN = $dbsn;
        $this->makeMembers();
        parent::__construct($this->getPDODSN(),$this->Username,$this->Password);
    }

    /**
     * Cria os membros das informações baseado no DBSN (Database Source Name)
     * informado na classe.
     */
    private function makeMembers()
    {
        //String DBSN local que será extraída
        $dbsn_local = $this->DBSN;
        
        //Extraindo o ENGINE
        if (ereg ("^(.+)://(.+)$", $dbsn_local, $parts))
        {
            $this->Engine   = $parts[1];
            $dbsn_local = $parts[2];
        }else
            return;

        //Extraindo USERNAME e PASSWORD
        if (ereg ("^(.+)@(.+)$", $dbsn_local, $parts))
        {
            $dbsn_local = $parts[2];

            if (ereg ("^(.+):(.+)$", $parts[1], $parts))
            {
                $this->Username = $parts[1];
                $this->Password = $parts[2];
            }else
                $this->Username = $parts[1];   
        }

        /**
         * Substituindo o "..bancodedados/?param1=value1..."
         *            por "..bancodedados?param1=value1..."
         *
         * para garantir uma avaliação mais facilitada para porta e parâmetros.
         */
        $dbsn_local = ereg_replace("^(.+)(\/\?)(.+)$","\\1?\\3",$dbsn_local);
        
        //Extraindo HOSTNAME e PORT
        if (ereg ("^(.+)/(.+)$", $dbsn_local, $parts))
        {
            $dbsn_local = $parts[2];

            if (ereg ("^(.+):(.+)$", $parts[1], $parts))
            {
                $this->Hostname = $parts[1];
                $this->Port     = $parts[2];
            }else
                $this->Hostname = $parts[1];
        }

        //Extraindo DATABASE e PARAMS
        if (ereg ("^(.+)([?])(.+)$", $dbsn_local, $parts))
        {
            $this->Database = $parts[1];
            $this->Params   = $parts[3];
        }else
            $this->Database = $dbsn_local;
        
        //Explodindo os parâmetros
        if ($this->Params)
        {
            $this->Params = split ("&", $this->Params);
            
            $params = $this->Params;
            $this->Params = array ();
            
            foreach ($params as $param)
            {
                $param = split ("=",$param);
                $this->Params = array_merge ($this->Params,array (trim ($param[0]) => trim ($param[1])));
            }
        }
        
        //Extraindo os parâmetros específicos do PDO
        $this->extractPDOParams();
        
        //Tirando a barra do final de Database se existir
        $this->Database = ereg_replace ("^(.+)/$","\\1", $this->Database);
    }
    
    /**
     * Extrai os parâmetros PDO da lista de parâmetros normais $Params
     * e coloca-os na lista de parâmetros específicos do PDO $PDOParams.
     *
     * Todo parâmetro com o prefixo "PDO::" é considerado um parâmetro
     * específico do PDO.
     */
    private function extractPDOParams()
    {
        //Backup dos parâmetros
        $backup_params      = $this->Params;
        $this->Params       = array ();
        $this->PDOParams    = array ();
        
        foreach ($backup_params as $param => $value)
        {
            if (ereg ("^([PDO::])(.+)$",$param))
                $this->PDOParams = array_merge ($this->PDOParams,
                    array ($param => $value));
            else
                $this->Params = array_merge ($this->Params,
                    array ($param => $value));
        }
    }
    
    /**
     * Retorna um DSN compatível com o padrão usado na classe PDO
     */
    private function getPDODSN()
    {
        //DSN padrão para todos os Engines
        $dsn        = $this->Engine . ':';
        
        if ($this->Hostname)
            $dsn   .= 'host=' . $this->Hostname . ';';
        
        if ($this->Database)
            $dsn   .= 'dbname=' . $this->Database . ';';
           
        if ($this->Port)
            $dsn   .= 'port=' . $this->Port . ';';
            
        //Parâmetros adicionais
        foreach ($this->Params as $k => $v)
        {
            if ($k && $v)
                $dsn   .= (constant($k) ? constant($k) : $k)
                       . '=' 
                       . (constant($v) ? constant($v) : $v)
                       . ';';
        }
        
        //Retirando o ; (ponto e vírgula) do final da String $dsn caso exista
        if (ereg ("^(.+)([;])$",$dsn,$parts))
            $dsn = $parts[1];
            
        return $dsn;
    }
    
    /** 
     * Getter para $DBSN
     */
    public function getDBSN()
    {
        return $this->DBSN;
    }

    /** 
     * Getter para $Engine
     */
    public function getEngine()
    {
        return $this->Engine;
    }

    /** 
     * Getter para $Hostname
     */
    public function getHostname()
    {
        return $this->Hostname;
    }

    /** 
     * Getter para $Port
     */
    public function getPort()
    {
        return $this->Port;
    }

    /** 
     * Getter para $Database
     */
    public function getDatabase()
    {
        return $this->Database;
    }

    /** 
     * Getter para $Username
     */
    public function getUsername()
    {
        return $this->Username;
    }

    /** 
     * Getter para $Password
     */
    public function getPassword()
    {
        return $this->Password;
    }

    /**
     * Getter para $Params
     */
    public function getParams()
    {
        return $this->Params;
    }
    
    /**
     * Getter para $PDOParams
     */
    public function getPDOParams()
    {
        return $this->PDOParams;
    }
}
?>
